import time
from tempfile import gettempdir
import hashlib
from fastapi import Query, Body, APIRouter, UploadFile, Form, Depends, Request

from components.router import CenterRoute
from config import aredis
from controller.activity_register import crud_role_related_alumni
from controller.alumni import parse_alumni_search_results, drop_alumni
from ioservice.alumni import *
from ioservice.alumni.alumni_reward import get_reward_by_context, bulk_update_rewards, bulk_insert_rewards, \
    drop_rewards_by_reward_ids, add_alumni_to_rewards, drop_alumni_from_rewards
from models.activity import RoleAlumni
from models.alumni import *
from models.alumni_extra import AlumniExtraDataSearchModel, AlumniExtraDataModel, AlumniExtraDataType, \
    ValidateAlumniFieldModel
from schemas.search import QuerySearchParam
from task_utils.task_es import job_delete_alumni, job_update_alumni_to_es, workflow_creat_edit_alumni, \
    workflow_delete_alumni
from task_utils.task_jobs import job_join_act_contacts, job_update_alumni_completion_score, job_alumni_join_act, \
    job_update_ranking, job_update_ranking_v2
from utils.dateutil import date_now
from utils.deps import validate_perms

router = APIRouter(route_class=CenterRoute)


@router.get("/alumni/{alumni_id}/belong", description='人才属于的部门')
async def alumni_belong(alumni_id: str):
    res = await get_belong_by_alumni(alumni_id)
    return res


@router.post('/alumni/search', description="人才列表/过滤")
async def api_search_alumni(request: Request, context: AlumniSearchModel, permission_ids: Optional[str] = Query(default=None)):
    visibility_ids = permission_ids.split(",") if permission_ids else []

    total, results = await search_alumni(context, visibility_ids)
    await parse_alumni_search_results(results, context)
    job_update_ranking.apply_async()
    job_update_ranking_v2.apply_async()
    return {"total": total, "data": results}


@router.get('/alumni')
async def api_get_alumni(name: Optional[str] = Query(None), offset: int = Query(0, gte=0),
                         size: int = Query(20, le=100),
                         institution_ids: Optional[List[str]] = Depends(validate_perms)):
    res = await get_alumni(alumni_name=name, institution_ids=institution_ids, skip=offset, limit=size)
    return res


@router.post('/alumni/{alumni_id}/info')
async def api_get_alumni_by_id(alumni_id: OID, activity_id: Union[str, None] = None,
                               fields: Optional[List[str]] = Body(None, embed=True)):
    res = await get_alumni_by_id(alumni_id, activity_id, projection=fields)
    return res


@router.post("/alumni/id_photo")
async def api_upload_id_photo(photo_file: UploadFile = Form(...)):
    photo_file_type = ["bmp", "jpg", "png", "tif", "gif", "pcx", "tga", "exif", "fpx", "svg", "psd", "cdr", "pcd",
                       "dxf", "ufo", "eps", "ai", "raw", "WMF", "webp", "avif", "apng"]
    temp_dir = gettempdir()
    file_path = os.path.join(temp_dir, str(hash(date_now().timestamp())) + "_" + photo_file.filename)
    if not any(photo_file.filename.endswith(file_type) for file_type in photo_file_type):
        raise InvalidParam("file", "must be picture")
    try:
        with open(file_path, "wb") as f:
            f.write(photo_file.file.read())
        return file_path
    except Exception as e:
        logger.error(f"upload photo failed! {photo_file.filename}; Exception: {e}")
        raise InvalidParam("file", "must be picture")


@router.post('/alumni/bulk', description="创建人才")
async def api_bulk_insert_alumni(data: List[AlumniInsertModel] = Body(..., embed=True),
                                 role_by: Optional[RoleAlumni] = None
                                 ):
    res = await bulk_insert_alumni(data)  # set()
    if role_by and res:
        role_by.person_id = list(res)[0]
        await crud_role_related_alumni(role_by)

    # 更新人参与的活动数量
    workflow_creat_edit_alumni.apply_async(args=[res], queue='es')
    return res


@router.put("/alumni/bulk")
async def api_update_alumni(alumnis: List[AlumniUpdateModel] = Body(..., embed=True),
                            role_by: Optional[RoleAlumni] = None,
                            action: Optional[str] = "batches"
                            ):
    """
    根据person_id更新对校友
    # fixme
    覆盖和非覆盖的2个模式
    xyk: force overwrite
    xyk-simple: exclude none
    """

    for alumnus in alumnis:
        alumnus.fields_check(include=["id"], exclude=["ocode", "ocode_num", "create_time"])
    res = await bulk_update_alumni(alumnis, action)
    if role_by:
        await crud_role_related_alumni(role_by)

    # 检查完整度
    update_alumni_ids = [str(a.id) for a in alumnis]
    job_update_alumni_completion_score.apply_async(args=[update_alumni_ids], countdown=120)
    # 更新 es
    workflow_creat_edit_alumni.apply_async(args=[update_alumni_ids], queue='es')
    return res


@router.delete('/alumni/bulk')
async def api_delete_alumni(ids: List[str] = Body(..., embed=True)):
    """
    根据person_id删除对应校友
    TODO 必须先删除所有与校友-关系的数据
    """

    res = await drop_alumni(ids)
    # 更新排行表
    workflow_delete_alumni.apply_async(args=[ids], queue='es')
    job_update_ranking.apply_async(countdown=15, expires=120)
    job_update_ranking_v2.apply_async(countdown=18, expires=120)
    return res


@router.post("/alumni/reward/search")
async def api_get_alumni_reward(context: AlumniExtraDataSearchModel):
    total, res = await get_reward_by_context(context)
    return {"total": total, "data": res}


class AlumniExtraDataParam(AlumniExtraDataSearchModel):
    rewards: Optional[List[dict]]


@router.post("/alumni/reward/update")
async def api_update_alumni_reward(context: AlumniExtraDataParam):
    if not context.check_info_type(AlumniExtraDataType.reward):
        raise InvalidParam("data_type", "must be reward")
    reward_col = context.set_data_col()
    res = await bulk_update_rewards(reward_col, context.data_model, context.rewards)
    return res


@router.put("/alumni/reward/insert")
async def api_create_alumni_reward(context: AlumniExtraDataParam):
    if not context.check_info_type(AlumniExtraDataType.reward):
        raise InvalidParam("data_type", "must be reward")
    reward_col = context.set_data_col()
    res = await bulk_insert_rewards(reward_col=reward_col, reward_model=context.data_model, rewards=context.rewards)
    return res


class DeleteParam(AlumniExtraDataModel):
    rewards: List[OID]


@router.delete("/alumni/reward/delete")
async def api_drop_alumni_reward(context: DeleteParam):
    reward_col = context.set_data_col()
    res = await drop_rewards_by_reward_ids(reward_col, context.rewards)
    return res


class LinkType(Enum):
    link = "link"
    unlink = "unlink"


class RewardLinkParam(AlumniExtraDataModel):
    opt: LinkType
    alumni_ids: Optional[List[OID]]
    reward_ids: Optional[List[OID]]


@router.post("/alumni/reward/link")
async def api_link_alumni_reward(context: RewardLinkParam):
    reward_col = context.set_data_col()
    if context.opt == LinkType.link:
        res = await add_alumni_to_rewards(reward_col, alumni_ids=context.alumni_ids, reward_ids=context.reward_ids)
    else:
        res = await drop_alumni_from_rewards(reward_col, alumni_ids=context.alumni_ids, reward_ids=context.reward_ids)
    return res


@router.get("/alumni/{alumni_id}/work/{doc_id}")
async def api_get_alumni_work(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_work(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/work/search")
async def api_get_alumni_work(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_work_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/work")
async def api_update_alumni_work(alumni_id: OID, data: List[AlumniWorkModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_work(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/work")
async def api_insert_alumni_work(alumni_id: OID, data: List[AlumniWorkModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_work(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/work")
async def api_drop_alumni_work(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_work(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/position/{doc_id}")
async def api_get_alumni_position(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_position(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/position/search")
async def api_get_alumni_position(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_position_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/position")
async def api_update_alumni_position(alumni_id: OID, data: List[AlumniPositionModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_position(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/position")
async def api_insert_alumni_position(alumni_id: OID, data: List[AlumniPositionModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_position(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/position")
async def api_drop_alumni_position(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_position(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/education/{doc_id}")
async def api_get_alumni_education(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_education(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/education/search")
async def api_get_alumni_education(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_education_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/education")
async def api_update_alumni_education(alumni_id: OID, data: List[AlumniEducationModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_education(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/education")
async def api_insert_alumni_education(alumni_id: OID, data: List[AlumniEducationModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_education(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/education")
async def api_drop_alumni_education(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_education(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/journal_paper/{doc_id}")
async def api_get_alumni_journal_paper(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_journal_paper(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/journal_paper/search")
async def api_get_alumni_journal_paper(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_journal_paper_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/journal_paper")
async def api_update_alumni_journal_paper(alumni_id: OID, data: List[AlumniJournalPaperModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_journal_paper(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/journal_paper")
async def api_insert_alumni_journal_paper(alumni_id: OID, data: List[AlumniJournalPaperModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_journal_paper(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/journal_paper")
async def api_drop_alumni_journal_paper(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_journal_paper(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/conference_paper/{doc_id}")
async def api_get_alumni_conference_paper(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_conference_paper(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/conference_paper/search")
async def api_get_alumni_conference_paper(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_conference_paper_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/conference_paper")
async def api_update_alumni_conference_paper(alumni_id: OID,
                                             data: List[AlumniConferencePaperModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_conference_paper(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/conference_paper")
async def api_insert_alumni_conference_paper(alumni_id: OID,
                                             data: List[AlumniConferencePaperModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_conference_paper(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/conference_paper")
async def api_drop_alumni_conference_paper(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_conference_paper(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/activity_patent/{doc_id}")
async def api_get_alumni_activity_patent(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_activity_patent(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/activity_patent/search")
async def api_get_alumni_activity_patent(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_activity_patent_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/activity_patent")
async def api_update_alumni_activity_patent(alumni_id: OID,
                                            data: List[AlumniActivityPatentModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_activity_patent(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/activity_patent")
async def api_insert_alumni_activity_patent(alumni_id: OID,
                                            data: List[AlumniActivityPatentModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_activity_patent(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/activity_patent")
async def api_drop_alumni_activity_patent(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_activity_patent(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/academic_project/{doc_id}")
async def api_get_alumni_academic_project(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_academic_project(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/academic_project/search")
async def api_get_alumni_academic_project(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_academic_project_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/academic_project")
async def api_update_alumni_academic_project(alumni_id: OID,
                                             data: List[AlumniAcademicProjectModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_academic_project(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/academic_project")
async def api_insert_alumni_academic_project(alumni_id: OID,
                                             data: List[AlumniAcademicProjectModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_academic_project(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/academic_project")
async def api_drop_alumni_academic_project(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_academic_project(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/construction_project/{doc_id}")
async def api_get_alumni_construction_project(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_construction_project(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/construction_project/search")
async def api_get_alumni_construction_project(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_construction_project_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/construction_project")
async def api_update_alumni_construction_project(alumni_id: OID,
                                                 data: List[AlumniConstructionProjectModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_construction_project(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/construction_project")
async def api_insert_alumni_construction_project(alumni_id: OID,
                                                 data: List[AlumniConstructionProjectModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_construction_project(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/construction_project")
async def api_drop_alumni_construction_project(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_construction_project(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/paper/{doc_id}")
async def api_get_alumni_paper(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_paper(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/paper/search")
async def api_get_alumni_paper(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_paper_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/paper")
async def api_update_alumni_paper(alumni_id: OID, data: List[AlumniPaperModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_paper(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/paper")
async def api_insert_alumni_paper(alumni_id: OID, data: List[AlumniPaperModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_paper(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/paper")
async def api_drop_alumni_paper(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_paper(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/standard/{doc_id}")
async def api_get_alumni_standard(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_standard(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/standard/search")
async def api_get_alumni_standard(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_standard_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/standard")
async def api_update_alumni_standard(alumni_id: OID, data: List[AlumniStandardModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_standard(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/standard")
async def api_insert_alumni_standard(alumni_id: OID, data: List[AlumniStandardModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_standard(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/standard")
async def api_drop_alumni_standard(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_standard(alumni_id, ids)
    return res


@router.get("/alumni/{alumni_id}/award/{doc_id}")
async def api_get_alumni_award(alumni_id: OID, doc_id: OID, fields: Optional[List[str]] = None):
    res = await get_alumni_award(alumni_id, doc_id, projection=fields)
    return res


@router.post("/alumni/{alumni_id}/award/search")
async def api_get_alumni_award(alumni_id: OID, context: QuerySearchParam):
    res = await get_alumni_award_by_alumni_id(alumni_id, context)
    return res


@router.post("/alumni/{alumni_id}/award")
async def api_update_alumni_award(alumni_id: OID, data: List[AlumniAwardModel] = Body(..., embed=True)):
    success, error = await bulk_update_alumni_award(alumni_id, data)
    res = {"success": success}
    if error:
        res['error'] = error
    return res


@router.put("/alumni/{alumni_id}/award")
async def api_insert_alumni_award(alumni_id: OID, data: List[AlumniAwardModel] = Body(..., embed=True)):
    res = await bulk_insert_alumni_award(alumni_id, data)
    return res


@router.delete("/alumni/{alumni_id}/award")
async def api_drop_alumni_award(alumni_id: OID, ids: Optional[List[str]] = Body(..., embed=True)):
    res = await drop_alumni_award(alumni_id, ids)
    return res


@router.post("/alumni/check-in", description="权限检查")
async def alumni_check_by_ins(ids: Optional[List[str]] = Body(..., embed=True)):
    person_ids = await agg_user_has_persons(ids)
    return [str(_id) for _id in person_ids]


@router.post("/alumni/check/field", description="姓名+email")
async def validate_name_and_email(query: ValidateAlumniFieldModel):
    include_person_by_emails = await crud_validate_email(query.dict(exclude_none=True))
    return include_person_by_emails
